#include <math.h>
#include <opencv2\opencv.hpp>
#include "Recognizer.h"

#define VECTOR_MIN_LEN 5

bool inline vectorDown(BarVector &v1, BarVector &v2) {
    return (v1.module > v2.module);
}

Recognizer::Recognizer(){
   
}

Recognizer::~Recognizer(){
    if (fstm.is_open()){
        fstm.close();
    }
}

void Recognizer::step(uchar* ptr, int &s, int &w, int &h, Point2f &p1, Point2f &p2, float &dis){
    Point2f e1[8], e2[8];
    if (p1.x > 0 && p1.x < w - 1 && p1.y > 0 && p1.y < h - 1 && p2.x > 0 && p2.x < w - 1 && p2.y > 0 && p2.y < h - 1){
        e1[0].x = p1.x - 1;
        e1[0].y = p1.y - 1;
        e1[1].x = p1.x;
        e1[1].y = p1.y - 1;
        e1[2].x = p1.x + 1;
        e1[2].y = p1.y - 1;
        e1[3].x = p1.x - 1;
        e1[3].y = p1.y;
        e1[4].x = p1.x + 1;
        e1[4].y = p1.y;
        e1[5].x = p1.x - 1;
        e1[5].y = p1.y + 1;
        e1[6].x = p1.x;
        e1[6].y = p1.y + 1;
        e1[7].x = p1.x + 1;
        e1[7].y = p1.y + 1;

        e2[0].x = p2.x - 1;
        e2[0].y = p2.y - 1;
        e2[1].x = p2.x;
        e2[1].y = p2.y - 1;
        e2[2].x = p2.x + 1;
        e2[2].y = p2.y - 1;
        e2[3].x = p2.x - 1;
        e2[3].y = p2.y;
        e2[4].x = p2.x + 1;
        e2[4].y = p2.y;
        e2[5].x = p2.x - 1;
        e2[5].y = p2.y + 1;
        e2[6].x = p2.x;
        e2[6].y = p2.y + 1;
        e2[7].x = p2.x + 1;
        e2[7].y = p2.y + 1;
        float d = 0,max = 0;
        int index = 0;
        for (int i = 0; i < 8; i++){
            index = e1[i].y*s + e1[i].x;
            if (ptr[index] < 255 && ptr[index] < 255){
                d = P2P_DIS(e1[i], p2);
                if (d > max){
                    max = d;
                    p1 = e1[i];
                }
            }
            ptr[index] = 255;
        }
        for (int i = 0; i < 8; i++){
            index = e2[i].y*s + e2[i].x;
            if (ptr[index] < 255 && ptr[index] < 255){
                d = P2P_DIS(e2[i], p1);
                if (d > max){
                    max = d;
                    p2 = e2[i];
                }
            }
            ptr[index] = 255;
        }
        if (max > dis){
            dis = max;
            step(ptr, s, w, h, p1, p2, dis);
        }
    }
}

void Recognizer::extendLine(Mat &sub, vector<BarVector> &vs,const int &lenLimit){
    CvMat cv_img(sub);
    uchar* ptr = cv_img.data.ptr;
    int s = cv_img.step;
    int w = cv_img.cols;
    int h = cv_img.rows;
    for (int y = 0; y < cv_img.rows; y++)
    {
        for (int x = 0; x < cv_img.cols; x++)
        {
            if (ptr[y*s + x] == 0){
                Point2f p1(x, y);
                Point2f p2(x, y);
                float dis = 0;
                step(ptr, s, w, h, p1, p2, dis);
                if (dis >= lenLimit){
                    vs.push_back(BarVector{ p1, p2,dis});
                }
            }
        }
    }
    std::sort(vs.begin(), vs.end(), vectorDown);
}

void Recognizer::lineImage(Mat &image, vector<BarVector> &vs){
    float cut_scale = 0.1;
    Mat gray;
    Mat thresh;
    if (image.channels() == 1){
        gray = image;
    }
    else{
        cvtColor(image, gray, COLOR_BGR2GRAY);
    }
    Mat kernel = getStructuringElement(MORPH_RECT, Size(2, 2));
    adaptiveThreshold(gray, thresh, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);
    extendLine(thresh, vs, VECTOR_MIN_LEN);
}

void Recognizer::setDataInfoPath(string path){
    fstm = ofstream(path, ios::out);
    assert(fstm.is_open());
    fstm << "result,ratio,area,number,angle_var" << endl;
}

float Recognizer::vectorCos(const BarVector &v1, const BarVector &v2){
    Point2f p1 = v1.p2 - v1.p1;
    Point2f p2 = v2.p2 - v2.p1;
    float mult = p1.dot(p2);
    if (mult < 0){
        p1 = v1.p1 - v1.p2;
        mult = p1.dot(p2);
    }
    return mult / (sqrt(p1.x*p1.x + p1.y*p1.y)*sqrt(p2.x*p2.x + p2.y*p2.y));
}


void Recognizer::showLineImage(Mat &image){
    vector<BarVector> vs;
    lineImage(image,vs);
    for (BarVector v : vs){
        printf("dis=%f\n", v.module);
        line(image, v.p1, v.p2, Scalar(255, 0, 0));
    }
    imshow("showline", image);
}


void Recognizer::recognize(Mat &image, vector<RotatedRect> & retRect){
	int kernel_size = 10;
	int gauss_ksize = 5;
	Mat kernel = getStructuringElement(MORPH_RECT, Size(kernel_size, kernel_size));
	Mat gray_img, blur_img, thresh_img, thresh_gs_img, open_img;
	cvtColor(image, gray_img, COLOR_BGR2GRAY);
	GaussianBlur(gray_img, blur_img, Size(gauss_ksize, gauss_ksize), 0);
	threshold(blur_img, thresh_img, 0, 255, THRESH_BINARY + THRESH_OTSU);
	morphologyEx(thresh_img, open_img, MORPH_OPEN, kernel);
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	vector<RotatedRect> rotRects;
	vector<float> cntAreas;
	float area = 0;
	float sum = 0;
	float area_avg = 0;
	float count = 0;
	findContours(open_img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
	cntAreas.resize(contours.size());
	for (size_t i = 0; i < contours.size(); i++){
		if (hierarchy[i][2] == -1){
			area = contourArea(contours[i]);
			cntAreas[i] = area;
			sum += area;
			count++;
		}
		else{
			cntAreas[i] = 0;
		}
	}
	if (count > 0){
		area_avg = sum / count;
	}
	for (size_t i = 0; i < contours.size(); i++){
		if (cntAreas[i] > area_avg){
			RotatedRect r = minAreaRect(contours[i]);
			rotRects.push_back(r);
			Point2f vertices[4];
			r.points(vertices);
			line(image, vertices[0], vertices[1], Scalar(0, 255, 0));
			line(image, vertices[1], vertices[2], Scalar(0, 255, 0));
			line(image, vertices[2], vertices[3], Scalar(0, 255, 0));
			line(image, vertices[3], vertices[0], Scalar(0, 255, 0));
		}
	}

	for (size_t i = 0; i < rotRects.size(); i++){
		Point2f vertices[4];
		Rect r = rotRects[i].boundingRect();
		Mat roi = gray_img(r);
		Mat sub;
		roi.copyTo(sub);
		//lineImage(image, sub, r, rotRects[i]);
		//if (contour(image, sub, r, rotRects[i], 0, 0, 0, 0)){
		//	retRect.push_back(rotRects[i]);
		//}
	}
	imshow("src", image);
}

void Recognizer::imgSplit(Mat &image, vector<Mat> &subs){
    int kernel_size = 10;
    int gauss_ksize = 5;
    srcImg = image;
    Mat kernel = getStructuringElement(MORPH_RECT, Size(kernel_size, kernel_size));
    Mat gray_img, blur_img, thresh_img, thresh_gs_img, open_img;
    cvtColor(srcImg, gray_img, COLOR_BGR2GRAY);
    GaussianBlur(gray_img, blur_img, Size(gauss_ksize, gauss_ksize), 0);
    threshold(blur_img, thresh_img, 0, 255, THRESH_BINARY + THRESH_OTSU);
    morphologyEx(thresh_img, open_img, MORPH_OPEN, kernel);
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(open_img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    for (size_t i = 0; i < contours.size(); i++){
        if (hierarchy[i][2] == -1){
			//RotatedRect rot = minAreaRect(contours[i]);
			//Rect r = rot.boundingRect();
			Rect r = boundingRect(contours[i]);
			Mat roi = gray_img(r);
			Mat sub;
			roi.copyTo(sub);
			subs.push_back(sub);
        }
    }
}

void Recognizer::train(Mat &image, bool vaild){
    bool result = vaild;
    float ratio = (float)min(image.cols, image.rows) / (float)max(image.cols, image.rows);
    float area = image.cols * image.rows;
    int number = 0;
    float angle_var = 0;
    float var = 0;
    float cos = 0;
    float sum = 0;
    float mean = 0;
    vector<float> cs;
    BarVector axis{ Point2f(0, 0), Point2f(1, 0), 1};
    vector<BarVector> vs;
    lineImage(image, vs);
    if (vs.size() > 0){
        number = vs.size();
        for (BarVector v : vs){
            cos = vectorCos(v, axis);
            sum += cos;
            cs.push_back(cos);
        }
        mean = sum / cs.size();
        for (float c : cs){
            var += (c - mean)*(c - mean);
        }
        var = var / cs.size();
    }
    fstm << vaild << "," << ratio << "," << area << "," << number << "," << var << endl;
}

bool Recognizer::predict(Mat &image){
	return false;
}